package com.dbapp.common.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @program: data-sec-share
 * @description:
 * @author: caiqiang
 * @create: 2021-09-17 10:25
 **/
public class JwtUtils {

    public static final String TOKEN_HEADER_PREFIX = "Bearer ";
    public static final String TOKEN_HEADER = "System-Authorization";
    private static final String TOKEN_TYPE = "JWT";
    private static final String JWT_ID = UUID.randomUUID().toString();
    private static final String ISSUER = "GridSum.ltd";
    private static final String JWT_SECRET = "f79frr6wyf1x8nnzbp8aitb12iehuhksjlhvx61iw9szybq81fo1jx4ao4xjkb353qt5frpyxbibrbcydclk6y1rark20o5b26xtitnlesebh6q3wy44rjjhd0dyuint17b049grdrnucx344vzxb5fjrulowxnfcspjr9r2hjhy764u4zpzx9xtpdvimhfdfvamhyndxqkok9ej6hxu2mkw2nv0ufdhw35belxgptpx8i7hr4hx7pkwla4te2v0";

    public static String getToken(HttpServletRequest request) {
        return request.getHeader(TOKEN_HEADER);
    }

    /**
     * 解密jwt,(如果过期解析会报错)
     */
    public static Claims parseJWT(String token){
        SecretKey key = generalKey();   //签名秘钥，和生成的签名的秘钥一模一样
        return Jwts.parser()            //得到DefaultJwtParser
                .setSigningKey(key)     //设置签名的秘钥
                .parseClaimsJws(token)
               .getBody();
    }

    /**
     * 生成token
     */
    public static String generateToken(String userName,Long expiredTime) {
        Map<String, Object> claims = new HashMap<>();


        //从缓存中获取权限，token作为key和有效校验
        //claims.put(AUTHORITIES, authorities);

        Date now = new Date();
        // 过期时间,配置文件单位为分钟
        Date expirationTime = new Date(System.currentTimeMillis() + expiredTime * 60 * 1000 );
        //jwt不知道为啥，下面设置.setSubject(userName)    .setExpiration.不生效，其实他还是去 Map<String, Object> claims中获取的，并且key必须是这个指定的
        claims.put(Claims.SUBJECT, userName);
        claims.put(Claims.EXPIRATION, expirationTime);
        return Jwts.builder() // 这里其实就是new一个JwtBuilder，设置jwt的body
                .setHeaderParam("type", TOKEN_TYPE)
                .setId(JWT_ID)      // 设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuer(ISSUER)  // issuer：jwt签发人
                .setSubject(userName)// sub(Subject)：代表这个JWT的主体，即它的所有人，可以存放什么userId、username之类的，作为什么用户的唯一标志。
                .setNotBefore(now)
                .setIssuedAt(now)           // iat: jwt的签发时间
                .setExpiration(expirationTime)
                .setClaims(claims)
                .signWith(generalKey(),SignatureAlgorithm.HS512) // 设置签名使用的签名算法和签名使用的秘钥
                .compact();
    }

    /**
     * 是否需要根据配置最大刷新toekn时间，进行token刷新
     * @param claims
     * @param refreshTokenMaxExpiredTime 单位分钟
     * @return
     */
    public static boolean refreshTokenExpiredTime(Claims claims,Long refreshTokenMaxExpiredTime){
        Date expiredDate = claims.getExpiration();
        Date now = new Date();
        if (expiredDate.before(now)){
            return false;
        }
        long minutes = ChronoUnit.MINUTES.between(Instant.ofEpochMilli(now.getTime()), Instant.ofEpochMilli(expiredDate.getTime()));

        if (minutes <= refreshTokenMaxExpiredTime){
            return true;
        }
        return false;
    }


    public static boolean isTokenExpired(String token){
        Date expiredDate = null;
        try {
            expiredDate = parseJWT(token).getExpiration();
        } catch (Exception e) {
            return true;
        }
        return expiredDate.before(new Date());
    }
    /**
     * 由字符串生成加密key
     * 可以加入缓存
     */
    private static SecretKey generalKey() {
        // 本地的密码解码
        byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);

        // 根据给定的字节数组使用AES加密算法构造一个密钥
        return new SecretKeySpec(encodedKey, 0, encodedKey.length,  "HmacSHA256");
    }
}
